//
// Created by QU on 24-6-25.
//
#include <iostream>

struct A
{
    char a1;
    int a2;
    double a3;
};

struct B
{
    short b1;
    bool b2;
    double b3;
};

int main()
{
    std::cout << "sizeof(A::a1) + sizeof(A::a2) + sizeof(A::a3) = "
        << sizeof(A::a1) + sizeof(A::a2) + sizeof(A::a3) <<
        std::endl;
    std::cout << "sizeof(B::b1) + sizeof(B::b2) + sizeof(B::b3) = "
        << sizeof(B::b1) + sizeof(B::b2) + sizeof(B::b3) <<
        std::endl;
    std::cout << "sizeof(A) = " << sizeof(A) << std::endl;
    std::cout << "sizeof(B) = " << sizeof(B) << std::endl;


    // 除用一些小技巧获取类型对齐字节长度之外，很多编译器还提供了一些扩展方法帮助我们获得类型的对齐字节长度，以MSVC和GCC为
    // 例，它们分别可以通过扩展关键字__alignof和__alignof__来获取数据类型的对齐字节长度：

    // MSVC
    // auto x1 = __alignof(int);
    // auto x2 = __alignof(void(*)());

    // GCC
    // auto x3 = __alignof__(int);
    // auto x4 = __alignof__(void(*)())

    // alignof运算符和我们前面提到的编译器扩展关键字__alignof、__alignof__用法相同，都是获得类型的对齐字节长度，比如：
    // auto x1 = alignof(int);
    // auto x2 = alignof(void(*)());

    // int a = 0;
    // auto x3 = alignof(a);              // *C++标准不支持这种用法
    /*
    请注意上面的第4句代码，alignof的计算对象并不是一个类型，而是一个变量。但是C++标准规定alignof必须是针对类型的。
    不过GCC扩展了这条规则，alignof除了能接受一个类型外还能接受一个变量，用GCC编译此段代码是可以编译通过的。阅读了第4章的读
    者可能会想到，我们只需要结合decltype，就能够扩展出类似这样的功能：
    */
    int a = 0;
    auto x3 = alignof(decltype(a));
    std::cout << x3 << std::endl;

    alignas(8) int a1 = 0;
    auto x4 = alignof(decltype(a1)); // 错误的返回4，而并非设置的8

    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    // alignof 和 alignas 是C++11引入的关键字，用于控制和查询类型和变量的内存对齐。
    // 内存对齐是指变量在内存中的地址必须是某个特定值的倍数。良好的内存对齐可以提高程序性能，特别是在处理硬件时。

    // alignof
    // alignof 是一个运算符，用于获取类型的对齐要求。对齐要求是一个整数值，表示该类型的对象在内存中的地址必须是这个值的倍数。

    /*
    struct alignas(16) MyStruct {
        int a;
        double b;
    };

    int main() {
        std::cout << "Alignment of int: " << alignof(int) << std::endl;
        std::cout << "Alignment of double: " << alignof(double) << std::endl;
        std::cout << "Alignment of MyStruct: " << alignof(MyStruct) << std::endl;

        return 0;
    }
    */
    /*
    输出示例
    Alignment of int: 4
    Alignment of double: 8
    Alignment of MyStruct: 16
    */


    // alignas
    // alignas 是一个说明符，用于指定类型或变量的对齐要求。它可以用于类型定义或变量声明，使编译器按指定的对齐要求分配内存。

    struct alignas(16) MyAlignedStruct
    {
        int a;
        double b;
    };
    // 对齐变量：
    // alignas(32) int myAlignedInt;
    // 对齐数组：
    // alignas(64) double myAlignedArray[10];

    /*
    #include <iostream>
    #include <cstddef>

    struct MyStruct {
        int a;
        double b;
    };

    int main() {
        alignas(16) MyStruct s;
        alignas(32) int x;

        std::cout << "Address of s: " << &s << std::endl;
        std::cout << "Address of x: " << &x << std::endl;
        std::cout << "Alignment of s: " << alignof(decltype(s)) << std::endl;
        std::cout << "Alignment of x: " << alignof(decltype(x)) << std::endl;

        return 0;
    }
    */
    /*
    Address of s: 0x7ffeee0c6e40
    Address of x: 0x7ffeee0c6e60
    Alignment of s: 16
    Alignment of x: 32
    */

    /*
    详细说明
    alignof
    alignof 返回类型的对齐要求，单位是字节。
    可以用于任何类型，包括内置类型和用户定义类型。
    语法：alignof(type) 或 alignof(expression)。
    alignas
    alignas 可以指定类型或变量的对齐要求，单位是字节。
    可以用于结构体、类、联合体、变量、数组等。
    语法：
    类型：struct alignas(N) MyStruct { /* ... #1# };
    变量：alignas(N) int myVar;
    如果指定的对齐要求小于类型的默认对齐要求，则会忽略指定的对齐要求，使用默认对齐要求。
    如果指定的对齐要求不是2的幂，或者大于实现定义的最大对齐限制，则编译器会报错。
    注意事项
    使用不当的对齐要求可能导致性能下降或程序错误。
    一般情况下，建议只在有特定需求时才使用 alignas 来调整对齐要求。
    对于大多数应用程序，默认的对齐要求已经足够好，不需要手动调整。
    通过 alignof 和 alignas，你可以精细控制内存对齐，提高程序的性能和兼容性。了解它们的用法和特性，对编写高性能和低级系统编程非常有帮助。
    */

    // 前面曾提到过内存分配器会按照std::max_align_t的对齐字节长度分配对象的内存空间。这一点在C++17标准中发生了改变，new
    // 运算符也拥有了根据对齐字节长度分配对象的能力。这个能力是通过让new运算符接受一个std::align_ val_t类型的参数来获得分配
    // 对象需要的对齐字节长度来实现的：
    /*
    void* operator new(std::size_t, std::align_val_t);
    void* operator new[](std::size_t, std::align_val_t);
    */
    // 编译器会自动从类型对齐字节长度的属性中获取这个参数并且传参，不需要额外的代码介入。
    union alignas(256) X
    {
        char a1;
        int a2;
        double a3;
    };

    X* x = new X();
    std::cout << "x = " << x << std::endl;
    std::cout << "sizeof(x) = " << sizeof(*x) << std::endl;

    MyAlignedStruct ms;
    // 使用MSVC的读者如果想获得变量的对齐，不妨使用编译器的扩展关键字__alignof：
    std::cout << "alignof(ms) = " << alignof(ms) << std::endl;          // gcc可以编译通过
    std::cout << "sizeof(ms) = " << sizeof(ms) << std::endl;

}
/*
g++ -std=c++11 test_new.cpp -o cpp11
./cpp11
x = 0x1071620

g++ -std=c++17 test_new.cpp -o cpp17
./cpp17
x = 0x1d1700
*/
// 我们发现在使用C++11标准的情况下，new分配的对象指针（0x1071620）并没有按照X指定的对齐字节长度（256字节）对齐，
// 而在使用C++17标准的情况下，new分配的对象指针（0x1d1700）正好为X指定的对齐字节长度。